package com.apache.tools;

/**
 * 日志输出代理类。
 * <p>
 * 主要完成业务代码和日志工具间的解耦，使切换日志工具更加方便。
 * <p>
 * 如：将Log4j改为common-logging或自己的独立实现，则只需调整该类即可。
 * <p>
 * 简化了调用方式：如使用log4j，通常用法是先生成一个实例，再调用输出方法。
 * 现在，只需直接使用 Logs.debug();等静态方法即可
 * 
 * @author zszhang
 * <p>
 * 2008-09-08
 * @version 1.0
 * 
 */
public class LogUtil {
	/**
	 * 是否打印日志，true表示打印日志，false表示不打印。
	 * <p>
	 * 开发阶段可以将其设为ture，运行阶段可以设为false
	 */
	private static boolean enabled = true;

	/** 是否进行源代码定位，ture表示输出源代码所在类以及代码行 */
	private static boolean showLocSrc = true;

	/** 指定的日志级别 */
	private static int level = 1;

	/** 日志级别：普通 */
	private static final int info = 1;

	/** 日志级别：调试 */
	private static final int debug = 2;

	/** 日志级别：警告 */
	private static final int warn = 3;

	/** 日志级别：错误 */
	private static final int error = 4;

	private static final int fatal = 5;

	/** 消息所属和消息内容的分隔符 */
	private static final String msgSplit = ":";

	/** 该类的名称，用于识别该类的堆栈 */
	private static final String thisClassName = LogUtil.class.getName();

	// /** 日志工具类别：System.out.println() */
	// private static int logType_system = 1;
	//
	// /** 日志类型：log4j */
	// private static int logType_log4j = 2;
	//
	// /** 日志类型：common-logging */
	// private static int logType_logging = 3;

	/** 默认输出日志的日志工具：log4j */
	private static org.apache.log4j.Logger logger = null;

	/**
	 * 默认的构造方法
	 */
	public LogUtil() {

	}

	/**
	 * 应该读取自己的配置文件，然后根据配置文件加载log4j的配置文件
	 */

	static {
		// 可以单独指定log4j的配置文件，也可以使用默认。
		String log4jFile = LogUtil.class.getResource("/").getPath() + "/" + "log4j.properties";
		System.out.println("log4jFile=" + log4jFile);
		org.apache.log4j.PropertyConfigurator.configure(log4jFile);

		// 得到logger实例，作为输出工具。
		// 此句作用是用一个输出实例，取代每个类里面的： Logger.getLogger(X.class)
		logger = org.apache.log4j.Logger.getLogger("");
	}

	/**
	 * 设置日志级别
	 * @param logLevel
	 */
	public static void setLogLevel(int logLevel) {
		if (logLevel < 1)
			logLevel = 1;
		if (logLevel > 4)
			logLevel = 4;
		level = logLevel;
	}

	/**
	 * 启动日志
	 */
	public static void startLog() {
		LogUtil.enabled = true;
	}

	/**
	 * 设置日志级别
	 */
	public static void stopLog() {
		LogUtil.enabled = false;
	}

	/**
	 * 根据日志级别，输出日志。
	 * <p>
	 * 如果要改变日志输出工具，
	 * <p>
	 * 如：由原来的log4j改为System.out.println()或logging，则只需改动此方法即可。
	 * 
	 * @param level   日志级别
	 * @param message 日志消息
	 * @param ste     堆栈信息。
	 *<p>
	 *如果不需要输出源代码信息，则只需将静态成员属性 showLocSrc设为false即可。
	 */
	private static void log(int level, Object message, StackTraceElement[] ste) {
		String msg = "";
		if (message != null) {
			msg = message.toString();
		}

		if (ste != null) {
			// 加入源代码定位

			msg = getStackMsg(ste) + msgSplit + msg;
		}

		// 转入具体实现，此处为log4j，可以改为其他不同的日志实现。

		switch (level) {
		case info:
			logger.info(msg);
			break;
		case debug:
			logger.debug(msg);
			break;
		case warn:
			logger.warn(msg);
			break;
		case error:
			logger.error(msg);
			break;
		case fatal:
			logger.fatal(msg);
			break;
		default:
			logger.debug(msg);
		}
	}

	/**
	 * 根据堆栈信息得到源代码行信息
	 * 
	 * <p>原理：本工具类的堆栈下一行即为源代码的最原始堆栈。
	 * 
	 * @param ste     堆栈信息
	 * @return String 调用输出日志的代码所在的类.方法.代码行的相关信息
	 * 
	 * <p>如：com.MyClass 类里的 fun()方法调用了Logs.debug("test");
	 * <p>则堆栈信息为: com.MyClass.fun(MyClass.java 代码行号)
	 */
	private static String getStackMsg(StackTraceElement[] ste) {
		if (ste == null)
			return null;

		boolean srcFlag_temp = false;
		for (int i = 0; i < ste.length; i++) {
			StackTraceElement s = ste[i];
			if (srcFlag_temp) {
				//如果上一行堆栈代码是本类的堆栈，则该行代码则为源代码的最原始堆栈。

				return s == null ? "" : s.toString();
			}
			// 定位本类的堆栈

			if (thisClassName.equals(s.getClassName())) {
				srcFlag_temp = true;
			}
		}
		return null;
	}

	/**是否进行源代码定位：true，是；false，否
	 * 
	 * return boolean 为true表示进行源代码定位；为false表示不进行源代码定位
	 */
	public static boolean isShowLocSrc() {
		return showLocSrc;
	}

	/**设置是否进行源代码定位：true，是；false，否
	 * 
	 * @param showLocSrc 
	 */
	public static void setShowLocSrc(boolean showLocSrc) {
		LogUtil.showLocSrc = showLocSrc;
	}

	/**
	 * 输出info信息。synchronized
	 * 
	 * @param message 要输出的信息
	 */
	public static synchronized void info(Object message) {
		// 如果禁止日志或者输出级别不符，则返回。

		if (!enabled || info < level)
			return;

		if (showLocSrc) {
			log(info, message, Thread.currentThread().getStackTrace());
		} else {
			log(info, message, null);
		}
	}

	/**
	 * 输出debug信息
	 * 
	 * @param message 输入的debug信息
	 */
	public static synchronized void debug(Object message) {
		// 如果禁止日志或者输出级别不符，则返回。

		if (!enabled || debug < level)
			return;
		if (showLocSrc) {
			log(debug, message, Thread.currentThread().getStackTrace());
		} else {
			log(debug, message, null);
		}
	}

	/**
	 * 输出warn信息
	 * 
	 * @param message 输出warn信息
	 */
	public static synchronized void warn(Object message) {
		// 如果禁止日志或者输出级别不符，则返回。

		if (!enabled || warn < level)
			return;
		if (showLocSrc) {
			log(warn, message, Thread.currentThread().getStackTrace());
		} else {
			log(warn, message, null);
		}
	}

	/**
	 * 输出error信息
	 * 
	 * @param message 输出error信息
	 */
	public static synchronized void error(Object message) {
		// 如果禁止日志或者输出级别不符，则返回。

		if (!enabled || error < level)
			return;

		if (showLocSrc) {
			log(error, message, Thread.currentThread().getStackTrace());
		} else {
			log(error, message, null);
		}
	}

	/**
	 * 输出error信息
	 * 
	 * @param message 输出error信息
	 * @param re      运行时异常
	 */
	public static synchronized void error(Object message, RuntimeException re) {
		// 如果禁止日志或者输出级别不符，则返回。

		if (!enabled || error < level)
			return;

		if (showLocSrc) {
			log(error, message, Thread.currentThread().getStackTrace());
		} else {
			log(error, message, null);
		}
	}

	/**
	 * 输出error信息
	 * 
	 * @param message 输出error信息
	 * @param re      运行时异常
	 */
	public static synchronized void fatal(Object message, RuntimeException re) {
		// 如果禁止日志或者输出级别不符，则返回。

		if (!enabled || fatal < level)
			return;

		if (showLocSrc) {
			log(fatal, message, Thread.currentThread().getStackTrace());
		} else {
			log(fatal, message, null);
		}
	}

}
